﻿#pragma once

#include <queue>
#include <map>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <string>
#include <initializer_list>

#include "noncopyobj.h"
#include "string_builder.h"

namespace as
{
	namespace thread
	{
		class ThreadPool;
	}

	namespace tools
	{
		enum
		{
			LOG_TYPE_START = 0,
			LOG_TYPE_INFO = 0,
			LOG_TYPE_WARNING = 1,
			LOG_TYPE_ERROR = 2,
			LOG_TYPE_FATAL = 3,
			LOG_TYPE_MAX,
		};

		class LogStream : public as::noncopyobj
		{
		public:
			const std::string GetString()
			{
				return buffer_.GetString();
			}

			template<typename T>
			LogStream& operator<< (const T & val)
			{
				buffer_ << val;
				return *this;
			}

		private:
			StringBuilder<char> buffer_;
		};

		class LogEmptyStream : public as::noncopyobj
		{
		public:
			template<typename T>
			LogEmptyStream& operator<<(const T & val)
			{
				std::cout << val;
				return *this;
			}
		};

		class LogBase : public as::noncopyobj
		{
		public:
			LogBase(const std::string_view& path, int line, uint8_t type = as::tools::LOG_TYPE_INFO);
			~LogBase();
			LogStream& stream();
			LogEmptyStream empty_stream()
			{
				return LogEmptyStream();
			}
			LogStream stream_;
			uint8_t type_ = LOG_TYPE_INFO;
		};

		class Log
		{
		public:
			typedef std::mutex Mutex;
			struct LogTypeStr
			{
				uint8_t type = LOG_TYPE_INFO;
				std::string str;
			};
			Log();
			~Log();

			static Log *get_ins()
			{
				if (log_inc == nullptr)
					log_inc = new Log;
				return log_inc;
			}

			static void destory()
			{
				if (log_inc)
				{
					log_inc->stop();
					delete log_inc;
				}
				log_inc = nullptr;
			}

			std::string get_format(const char *_format, va_list &ap);
			size_t log_format(const char *, ...);
			size_t log_err_format(const char *, ...);
			size_t log_warn_format(const char *, ...);
			size_t log_fatal_format(const char *, ...);
			std::string tostring(int type);

			int async_info(uint8_t type, const char *str, ...);
			void stop();
			void run();
			void run_one();

			void HandleTask(bool isQuit = false);
			void SetBaseName(const std::string_view &name)
			{
				base_name_ = name;
			}
			bool WriteFile(uint8_t type, const std::string &str);

			LogBase GetInfoLog(const std::string_view &str, int line, uint8_t type)
			{
				return LogBase(str, line, type);
			}

			static std::function<void(const std::string_view &str)> g_output[LOG_TYPE_MAX];
		private:
			std::thread work_thread_;
			std::queue<LogTypeStr> log_queue_;
			Mutex m_;
			std::condition_variable expired_cond_;
			bool is_stop_ = false;
			std::string base_name_;
			std::string log_name_[LOG_TYPE_MAX];
			uint8_t console_output_type_ = LOG_TYPE_INFO;
			static Log *log_inc;
		};

	}
}

#define LOG_BASE(TYPE) as::tools::Log::get_ins()->GetInfoLog(__FILE__, __LINE__, TYPE).stream()
#define LOG_INFO LOG_BASE(as::tools::LOG_TYPE_INFO) 
#define LOG_ERROR LOG_BASE(as::tools::LOG_TYPE_ERROR) 
#define LOG_WARNING LOG_BASE(as::tools::LOG_TYPE_WARNING) 
#define LOG_FATIL LOG_BASE(as::tools::LOG_TYPE_FATAL) 
#define LOG_IF_FAILT(cond) if(!cond) LOG_FATIL

#ifdef _DEBUG
#define LOG_DEBUG LOG_INFO
#else
#define LOG_DEBUG if(false) LOG_INFO
#endif


